{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## 决策树\n",
    "&emsp;&emsp;上篇主要介绍和讨论了线性模型。首先从最简单的最小二乘法开始，讨论输入属性有一个和多个的情形，接着通过广义线性模型延伸开来，将预测连续值的回归问题转化为分类问题，从而引入了对数几率回归，最后线性判别分析LDA将样本点进行投影，多分类问题实质上通过划分的方法转化为多个二分类问题进行求解。本篇将讨论另一种被广泛使用的分类算法--决策树（Decision Tree）。\n",
    "### 决策树基本概念\n",
    "&emsp;&emsp;顾名思义，决策树是基于树结构来进行决策的，在网上看到一个例子十分有趣，放在这里正好合适。现想象一位捉急的母亲想要给自己的女娃介绍一个男朋友，于是有了下面的对话：\n",
    ">女儿：多大年纪了？  \n",
    " 母亲：26。  \n",
    " 女儿：长的帅不帅？  \n",
    " 母亲：挺帅的。  \n",
    " 女儿：收入高不？  \n",
    " 母亲：不算很高，中等情况。  \n",
    " 女儿：是公务员不？  \n",
    " 母亲：是，在税务局上班呢。  \n",
    " 女儿：那好，我去见见。\n",
    "\n",
    "&emsp;&emsp;这个女孩的挑剔过程就是一个典型的决策树，即相当于通过年龄、长相、收入和是否公务员将男童鞋分为两个类别：见和不见。假设这个女孩对男人的要求是：30岁以下、长相中等以上并且是高收入者或中等以上收入的公务员，那么使用下图就能很好地表示女孩的决策逻辑（即一颗决策树）。\n",
    "<br/><center>\n",
    "<img style=\"border-radius: 0.3125em;box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);\" src=\"../images/4-1-Girls'picking-Process.png\"><br><div style=\"color:orange; border-bottom: 1px solid #d9d9d9;display: inline-block;color: #000;padding: 2px;\">图4-1 女孩的挑剔过程</div></center>\n",
    "\n",
    "&emsp;&emsp;在上图的决策树中，决策过程的每一次判定都是对某一属性的“测试”，决策最终结论则对应最终的判定结果。一般一颗决策树包含：一个根节点、若干个内部节点和若干个叶子节点，易知：\n",
    "\n",
    "- 每个非叶节点表示一个特征属性测试。\n",
    "- 每个分支代表这个特征属性在某个值域上的输出。\n",
    "- 每个叶子节点存放一个类别。\n",
    "- 每个节点包含的样本集合通过属性测试被划分到子节点中，根节点包含样本全集。\n",
    "\n",
    "### 决策树的构造\n",
    "&emsp;&emsp;决策树的构造是一个递归的过程，有三种情形会导致递归返回：  \n",
    "1. 当前结点包含的样本全属于同一类别，这时直接将该节点标记为叶节点，并设为相应的类别；\n",
    "2. 当前属性集为空，或是所有样本在所有属性上取值相同，无法划分，这时将该节点标记为叶节点，并将其类别设为该节点所含样本最多的类别；\n",
    "3. 当前结点包含的样本集合为空，不能划分，这时也将该节点标记为叶节点，并将其类别设为父节点中所含样本最多的类别。算法的基本流程如下图所示：\n",
    "> 输入：训练集$D=\\{(x_1,y_1),(x_2,y_2),\\dots,(x_m,y_m)\\}$  \n",
    "&emsp;&emsp;&emsp;属性集$A=\\{\\alpha_1,\\alpha_2,\\dots,\\alpha_d\\}$  \n",
    "过程：函数TreeGenerate($D$,$A$)  \n",
    "&nbsp;&nbsp;1: 生成结点node；  \n",
    "&nbsp;&nbsp;2: **if** $D$中样本全属于同一类别$C$ **then**  \n",
    "&nbsp;&nbsp;3: &nbsp;&nbsp;将node标记为$C$类叶结点；**return**（**终止条件1——最好的情形**）  \n",
    "&nbsp;&nbsp;4: **end if**  \n",
    "&nbsp;&nbsp;5: **if** $A = \\emptyset$ **OR** $D$中样本在$A$上取值相同 **then**  \n",
    "&nbsp;&nbsp;6: &nbsp;&nbsp;将node标记为叶结点，其类别标记为$D$中样本数最多的类；**return**（**终止条件2——属性用完或分不开情形，使用后验分布**）  \n",
    "&nbsp;&nbsp;7: **end if**  \n",
    "&nbsp;&nbsp;8: 从$A$中选择最优划分属性$\\alpha_*$；  \n",
    "&nbsp;&nbsp;9: **for** $\\alpha_*$ 的每一个值$\\alpha_*^v$ **do**（**若为连续值属性，则只有两个分支($\\leqslant与>$)**）  \n",
    "10: &nbsp;&nbsp;为node生成一个分支；令$D_v$表示$D$中在$\\alpha_*$上取值为$\\alpha_*^v$的样本子集；  \n",
    "11: &nbsp;&nbsp;**if** $D_v$为空 **then**  \n",
    "12: &nbsp;&nbsp;&nbsp;&nbsp;将分支结点标记为叶结点，其类别标记为$D$中样本最多的类;**return**（**终止条件3——分支为空，使用先验分布**）  \n",
    "13: &nbsp;&nbsp;**else**  \n",
    "14: &nbsp;&nbsp;&nbsp;&nbsp;以TreeGenerate($D_v,A \\ {\\alpha_*}$)为分支结点  \n",
    "15: &nbsp;&nbsp;**end if**（**若$\\alpha_*$为连续属性，则不用去除，寻找下一个最优化分点可继续作为子节点的划分属性**）  \n",
    "16: **end for**  \n",
    "输出：以node为根结点的一棵决策树\n",
    "\n",
    "&emsp;&emsp;可以看出：决策树学习的关键在于如何选择划分属性，不同的划分属性得出不同的分支结构，从而影响整颗决策树的性能。属性划分的目标是让各个划分出来的子节点尽可能地“纯”，即属于同一类别。因此下面便是介绍量化纯度的具体方法，决策树最常用的算法有三种：ID3，C4.5和CART。\n",
    "\n",
    "#### ID3算法\n",
    "&emsp;&emsp;ID3算法使用信息增益为准则来选择划分属性，“信息熵”(information entropy)是度量样本结合纯度的常用指标，假定当前样本集合D中第k类样本所占比例为pk，则样本集合D的信息熵定义为：\n",
    "$$\\text{Ent}(D)=-\\sum_{k=1}^{|\\mathcal{Y }| } p_{k} \\log_2 p_k$$值越大表示越混乱，易知只有一个类别时，信息熵为0。  \n",
    "&emsp;&emsp;假定通过属性划分样本集$D$，产生了$V$个分支节点，$v$表示其中第$v$个分支节点，易知：分支节点包含的样本数越多，表示该分支节点的影响力越大。故可以计算出划分后相比原始数据集$D$获得的“信息增益”（information gain）。\n",
    "$$\\text{Gain}(D, \\alpha)=\\text{Ent}(D)-\\sum_{v=1}^{V} \\frac{|D^{v}|}{|D|} \\text{Ent}(D^v)\n",
    "$$&emsp;&emsp;信息增益越大，表示使用该属性划分样本集D的效果越好，因此ID3算法在递归过程中，每次选择最大信息增益的属性作为当前的划分属性。\n",
    "\n",
    "#### C4.5算法\n",
    "&emsp;&emsp;ID3算法存在一个问题，就是偏向于取值数目较多的属性，例如：如果存在一个唯一标识，这样样本集$D$将会被划分为$|D|$个分支，每个分支只有一个样本，这样划分后的信息熵为零，十分纯净，但是对分类毫无用处。因此C4.5算法使用了“增益率”（gain ratio）来选择划分属性，来避免这个问题带来的困扰。首先使用ID3算法计算出信息增益高于平均水平的候选属性，接着C4.5计算这些候选属性的增益率，增益率定义为：\n",
    "$$\\text{Gain_ratio}(D, \\alpha)=\\frac{\\text{Gain}(D, \\alpha)}{\\text{IV}(\\alpha)}$$其中$$\n",
    "\\text{IV}(\\alpha)=-\\sum_{v=1}^V \\frac{|D^v|}{|D|} \\log_2 \\frac{|D^v|}{|D|}$$当$\\alpha$属性的取值越多时，$\\text{IV}(\\alpha)$值越大\n",
    "\n",
    "#### CART算法\n",
    "&emsp;&emsp;CART决策树使用“基尼指数”（Gini index）来选择划分属性，基尼指数反映的是从样本集$D$中随机抽取两个样本，其类别标记不一致的概率，因此$\\text{Gini}(D)$越小越好，基尼指数定义如下：\n",
    "$$\\begin{aligned} \\text{Gini}(D) \n",
    "&=\\sum_{k=1}^{|\\mathcal{Y}|} \\sum_{k' \\neq k} p_k p_{k'} \\\\\n",
    "&=1-\\sum_{k=1}^{|\\mathcal{Y}|} p_k^2 \n",
    "\\end{aligned}$$任取两个样本类别标记不一致的概率，越小表示集合越纯。  \n",
    "进而，使用属性$\\alpha$划分后的基尼指数为：\n",
    "$$\\text { Gini_index }(D, \\alpha)=\\sum_{v=1}^V \\frac{|D^v|}{|D|} \\text{Gini}(D^v)$$故选择基尼指数最小的划分属性。\n",
    "\n",
    "### 剪枝处理\n",
    "&emsp;&emsp;从决策树的构造流程中我们可以直观地看出：不管怎么样的训练集，决策树总是能很好地将各个类别分离开来，这时就会遇到之前提到过的问题：过拟合（overfitting），即太依赖于训练样本。剪枝（pruning）则是决策树算法对付过拟合的主要手段，剪枝的策略有两种如下：  \n",
    "\n",
    "- 预剪枝（prepruning）：在构造的过程中先评估，再考虑是否分支。\n",
    "- 后剪枝（post-pruning）：在构造好一颗完整的决策树后，自底向上，评估分支的必要性。\n",
    "\n",
    "&emsp;&emsp;评估指的是性能度量，即决策树的泛化性能。之前提到：可以使用测试集作为学习器泛化性能的近似，因此可以将数据集划分为训练集和测试集。预剪枝表示在构造数的过程中，对一个节点考虑是否分支时，首先计算决策树不分支时在测试集上的性能，再计算分支之后的性能，若分支对性能没有提升，则选择不分支（即剪枝）。后剪枝则表示在构造好一颗完整的决策树后，从最下面的节点开始，考虑该节点分支对模型的性能是否有提升，若无则剪枝，即将该节点标记为叶子节点，类别标记为其包含样本最多的类别。\n",
    "<br/><center>\n",
    "<img style=\"border-radius: 0.3125em;box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);\" src=\"../images/4-2-Unpruned-Decision-Tree.png\"><br><div style=\"color:orange; border-bottom: 1px solid #d9d9d9;display: inline-block;color: #000;padding: 2px;\">图4-2 未剪枝决策树</div></center>\n",
    "\n",
    "<br/><center>\n",
    "<img style=\"border-radius: 0.3125em;box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);\" src=\"../images/4-3-Prepruning-Decision-Tree.png\"><br><div style=\"color:orange; border-bottom: 1px solid #d9d9d9;display: inline-block;color: #000;padding: 2px;\">图4-3 预剪枝决策树</div></center>\n",
    "\n",
    "<br/><center>\n",
    "<img style=\"border-radius: 0.3125em;box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);\" src=\"../images/4-4-Post-Pruning-Decision-Tree.png\"><br><div style=\"color:orange; border-bottom: 1px solid #d9d9d9;display: inline-block;color: #000;padding: 2px;\">图4-4 后剪枝决策树</div></center>\n",
    "\n",
    "&emsp;&emsp;上图分别表示不剪枝处理的决策树、预剪枝决策树和后剪枝决策树。预剪枝处理使得决策树的很多分支被剪掉，因此大大降低了训练时间开销，同时降低了过拟合的风险，但另一方面由于剪枝同时剪掉了当前节点后续子节点的分支，因此预剪枝“贪心”的本质阻止了分支的展开，在一定程度上带来了欠拟合的风险。而后剪枝则通常保留了更多的分支，因此采用后剪枝策略的决策树性能往往优于预剪枝，但其自底向上遍历了所有节点，并计算性能，训练时间开销相比预剪枝大大提升。\n",
    "\n",
    "### 连续值与缺失值处理\n",
    "&emsp;&emsp;对于连续值的属性，若每个取值作为一个分支则显得不可行，因此需要进行离散化处理，常用的方法为二分法，基本思想为：给定样本集$D$与连续属性$\\alpha$，二分法试图找到一个划分点$t$将样本集$D$在属性$\\alpha$上分为$\\leqslant t$与$>t$。\n",
    "\n",
    "1. 首先将α的所有取值按升序排列，所有相邻属性的均值作为候选划分点（n-1个，n为α所有的取值数目）。\n",
    "2. 计算每一个划分点划分集合D（即划分为两个分支）后的信息增益。\n",
    "3. 选择最大信息增益的划分点作为最优划分点。\n",
    "$$\n",
    "\\begin{aligned} \\text{Gain}(D, \\alpha) \n",
    "&=\\max _{t \\in T_{\\alpha}} \\text{Gain}(D, \\alpha, t) \\\\ \n",
    "&=\\max _{t \\in T_{\\alpha}} \\text{Ent}(D)-\\sum_{\\lambda \\in\\{-,+\\}} \\frac{|D_{t}^{\\lambda}|}{|D|} \\text{Ent}(D_{t}^{\\lambda}) \\end{aligned}\n",
    "$$$\\displaystyle \\sum_{\\lambda \\in\\{-,+\\}} \\frac{|D_{t}^{\\lambda}|}{|D|} \\text{Ent}(D_{t}^{\\lambda})$表示**划分为两个分支**。 \n",
    "\n",
    "&emsp;&emsp;现实中常会遇到不完整的样本，即某些属性值缺失。有时若简单采取剔除，则会造成大量的信息浪费，因此在属性值缺失的情况下需要解决两个问题：（1）如何选择划分属性。（2）给定划分属性，若某样本在该属性上缺失值，如何划分到具体的分支上。假定为样本集中的每一个样本都赋予一个权重，根节点中的权重初始化为1，则定义：  \n",
    "**样本子集所占比例：**$\\displaystyle \\rho=\\frac{\\sum_{x \\in \\tilde{D}} w_x}{\\sum_{x \\in D} w_x} $  \n",
    "**样本子集每个类别的比例：**$\\displaystyle \\tilde{p}_k=\\frac{\\sum_{x \\in \\tilde{D}_k} w_x}{\\sum_{x \\in \\tilde{D}} w_x} \\quad(1 \\leqslant k \\leqslant |\\mathcal{Y}|)$  \n",
    "**每个分支所含样本比例：**$\\displaystyle \\tilde{r}_v=\\frac{\\sum_{x \\in \\tilde{D}_v} w_x}{\\sum_{x \\in \\tilde{D}} w_x} \\quad(1 \\leqslant v \\leqslant V)$  \n",
    "&emsp;&emsp;对于（1）：通过在样本集D中选取在属性α上没有缺失值的样本子集，计算在该样本子集上的信息增益，最终的信息增益等于该样本子集划分后信息增益乘以样本子集占样本集的比重。即：\n",
    "$$\n",
    "\\begin{aligned} \\text{Gain}(D, \\alpha) \n",
    "&=\\rho \\times \\text{Gain}(\\tilde{D}, \\alpha) \\\\ \n",
    "&=\\rho \\times \\left(\\text{Ent}(\\tilde{D})-\\sum_{v=1}^V \\tilde{r}_v \\text{Ent}(\\tilde{D}^v)\\right) \\end{aligned}$$其中$\\rho$表示**无缺失样本子集所占比重**，$\\text{Gain}(\\tilde{D}, \\alpha)$表示**在属性$\\alpha$上无缺失的样本子集**  \n",
    "&emsp;&emsp;对于（2）：若该样本子集在属性$\\alpha$上的值缺失，则将该样本以不同的权重（即每个分支所含样本比例）划入到所有分支节点中。该样本在分支节点中的权重变为：\n",
    "$w_x=w_x*\\tilde{r}_v$ **即以不同权重划分到所有分支节点中**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
